package com.wu.util;

import com.wu.pojo.Articles;
import com.wu.pojo.model.ArticleModel;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @program: blog
 * @author: wulei
 * @create: 2021-11-23 11:32
 */

/**
 * Lucene 工具类
 */
@Component
public class LuceneUtil {

    /**
     * 索引存放路径
     */
    @Value("${lucene.index.path}")
    private String indexPath;

    /**
     * 创建索引
     *
     * @param articles
     * @return
     */
    @SneakyThrows
    public void createIndex(Articles articles) {
        Document document = new Document();
        // 创建并添加字段信息。参数：字段的名称、字段的值、是否存储，这里选Store.YES代表存储到文档列表。Store.NO代表不存储
        document.add(new StringField("articleId", articles.getArticleId(), Field.Store.YES));
        document.add(new StringField("userId", articles.getUserId(), Field.Store.YES));
        document.add(new StringField("articleContent", articles.getArticleContent(), Field.Store.YES));
        document.add(new StringField("articleDate", articles.getArticleDate(), Field.Store.YES));
        document.add(new StringField("articleWay", articles.getArticleWay(), Field.Store.YES));
        // 这里我们title字段需要用TextField，即创建索引又会被分词。
        document.add(new TextField("articleTitle", articles.getArticleTitle(), Field.Store.YES));
        //2 索引目录类,指定索引在硬盘中的位置
        Directory directory = FSDirectory.open(new File(indexPath).toPath());
        //3 创建分词器对象
        //Analyzer analyzer = new StandardAnalyzer();
        Analyzer analyzer = new IKAnalyzer();
        //4 索引写出工具的配置对象
        IndexWriterConfig conf = new IndexWriterConfig(analyzer);
        //conf.setOpenMode(IndexWriterConfig.OpenMode.CREATE);//会删除之前的索引然后再创建
        //5 创建索引的写出工具类。参数：索引的目录和配置信息
        IndexWriter indexWriter = new IndexWriter(directory, conf);

        //6 把文档交给IndexWriter
        indexWriter.addDocument(document);
        //7 提交
        indexWriter.commit();
        //8 关闭
        indexWriter.close();
    }


    /**
     * 根据文字Id删除索引
     *
     * @param articleId
     */
    @SneakyThrows
    public void deleteIndex(String articleId) {
        // 创建目录对象
        Directory directory = FSDirectory.open(new File(indexPath).toPath());
        // 创建配置对象
        IndexWriterConfig conf = new IndexWriterConfig(new IKAnalyzer());
        // 创建索引写出工具
        IndexWriter writer = new IndexWriter(directory, conf);

        // 根据词条进行删除
        writer.deleteDocuments(new Term("articleId", articleId));

        // 根据query对象删除,如果ID是数值类型，那么我们可以用数值范围查询锁定一个具体的ID
        //		Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
        //		writer.deleteDocuments(query);

        // 删除所有
        //writer.deleteAll();
        // 提交
        writer.commit();
        // 关闭
        writer.close();
    }

    /**
     * 更新索引
     *
     * @param articles
     */
    @SneakyThrows
    public void updateIndex(Articles articles) {
        //创建目录对象
        FSDirectory directory = FSDirectory.open(new File(indexPath).toPath());
        //创建配置对象
        IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
        //创建索引写出工具
        IndexWriter writer = new IndexWriter(directory, config);
        //创建新的文档数据
        Document document = new Document();
        document.add(new StringField("articleId", articles.getArticleId(), Field.Store.YES));
        document.add(new StringField("userId", articles.getUserId(), Field.Store.YES));
        document.add(new StringField("articleContent", articles.getArticleContent(), Field.Store.YES));
        document.add(new StringField("articleDate", articles.getArticleDate(), Field.Store.YES));
        document.add(new StringField("articleWay", articles.getArticleWay(), Field.Store.YES));
        // 这里我们title字段需要用TextField，即创建索引又会被分词。
        document.add(new TextField("articleTitle", articles.getArticleTitle(), Field.Store.YES));
        /**
         *修改索引。参数：
         * 词条：根据这个词条匹配到的所有文档都会被修改
         * 文档信息：要修改的新的文档数据
         */
        writer.updateDocument(new Term("articleId", articles.getArticleId()), document);
        //提交
        writer.commit();
        //关闭
        writer.close();
    }

    /**
     * 查找文章
     *
     * @param keyword     查找关键字
     * @param currentPage 当前页
     * @param pageSize    每页大小
     * @param userId      用户Id
     * @param classify    可以传时间，也可以传分类
     * @return
     */
    @SneakyThrows
    public Map<String, Object> searchArticles(String keyword, Integer currentPage, Integer pageSize, String userId, String classify) {
        int start = (currentPage - 1) * pageSize;
        int end = start + pageSize;
        Map<String, Object> res = new HashMap<>();
        List<Articles> articlesList = new ArrayList<>();
        List<ArticleModel> articleModelList = null;
        Articles articles;

        // 目录对象
        Directory directory = FSDirectory.open(new File(indexPath).toPath());
        // 创建读取工具
        IndexReader reader = DirectoryReader.open(directory);
        // 创建搜索工具
        IndexSearcher searcher = new IndexSearcher(reader);
        Query query;
        if (StringUtils.isBlank(userId)) {
            //如果用户ID为空，就是查询全部的文章
            QueryParser parser = new QueryParser("articleTitle", new IKAnalyzer());
            query = parser.parse(keyword);
        } else {
            Term term = new Term("userId", userId);
            query = new TermQuery(term);
        }
        // 格式化器
        Formatter formatter = new SimpleHTMLFormatter("<span style='color: red'>", "</span>");
        QueryScorer scorer = new QueryScorer(query);
        //393765
        //558674
        // 准备高亮工具
        Highlighter highlighter = new Highlighter(formatter, scorer);
        highlighter.setTextFragmenter(new SimpleFragmenter(100000));
        // 搜索

        TopDocs topDocs = searcher.search(query, end);
        int totalPages = (int) ((topDocs.totalHits.value + pageSize - 1) / pageSize);
        if (totalPages == 0) {
            res.put("total", 0);
            res.put("totalPages", 0);
            res.put("article", new ArrayList<>());
            return res;
        }
        res.put("total", topDocs.totalHits.value);
        res.put("totalPages", totalPages);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        if (end > scoreDocs.length) {
            end = scoreDocs.length;
        }
        for (int i = start; i < end; i++) {
            ScoreDoc scoreDoc = scoreDocs[i];
            // 获取文档编号
            int docID = scoreDoc.doc;
            Document doc = reader.document(docID);
            if (doc.get("status").equals("0")) {
                continue;
            }
            articles = new Articles();
            articles.setArticleId(doc.get("articleId"));
            String articleTitle = doc.get("articleTitle");
            // 用高亮工具处理普通的查询结果,参数：分词器，要高亮的字段的名称，高亮字段的原始值
            String hTitle = highlighter.getBestFragment(new IKAnalyzer(), "articleTitle", articleTitle);
            articles.setArticleContent(hTitle);
            articlesList.add(articles);
            // 获取文档的得分
        }
        res.put("article", articleModelList);
        return res;
    }

}
